package concreto.redeneural;

import java.util.ArrayList;
import java.util.List;

import comportamento.neuronio.INeuronio;
import comportamento.neuronio.INeuronioAbstrato;
import comportamento.redeneural.ICamadaNeural;
import comportamento.redeneural.ICamadaNeuralFeedFoward;
import comportamento.redeneural.IRedeNeural;

import concreto.neuronio.NeuronioGenerico;
import concreto.neuronio.NeuronioMP;

public class RedeNeuralFeedFoward implements IRedeNeural{

    private List<ICamadaNeural> camadas = new ArrayList<ICamadaNeural>();
    private int indiceCamadas = 0;
    
    public INeuronio criarNeuronio() {

	//Neuronio neuronio = new NeuronioMP(); 
	return null;//neuronio;
    }

    public INeuronio criarNeuronioGenerico(double limiar, int funcaoAtivacao) {
	
	INeuronio neuronio = new NeuronioGenerico(limiar); 
	neuronio.selecionarFuncaoAtivacao(funcaoAtivacao);
	
	return neuronio;
    }    

    public INeuronio criarNeuronioMP(double limiar) {

	INeuronio neuronio = new NeuronioMP(limiar); 
	neuronio.selecionarFuncaoAtivacao(INeuronioAbstrato.ATIVACAO_DEGRAU);
	
	return neuronio;
    }
    
    public INeuronio criarNeuronio(int funcaoAtivacao) {

	INeuronio neuronio = new NeuronioMP(0);
	neuronio.selecionarFuncaoAtivacao(funcaoAtivacao);

	return neuronio;
    }

    public INeuronio criarNeuronio(Class<? extends INeuronio> classeConcreta) {

	INeuronio neuronio = null;
	try {

	    neuronio = classeConcreta.newInstance();

	} catch (InstantiationException e) {

	    e.printStackTrace();
	} catch (IllegalAccessException e) {

	    e.printStackTrace();
	} 

	return neuronio;
    }

    public INeuronio criarNeuronio(Class<? extends INeuronio> classeConcreta, int funcaoAtivacao) {
	
	INeuronio neuronio = null;
	
	try {

	    neuronio = classeConcreta.newInstance();
	    neuronio.selecionarFuncaoAtivacao(funcaoAtivacao);
	    
	} catch (InstantiationException e) {

	    e.printStackTrace();
	} catch (IllegalAccessException e) {

	    e.printStackTrace();
	} 

	return neuronio;
    }


    public double[] estimular(double[] estimulos) {

	double[] aux = estimulos;
	
	for(ICamadaNeural c : camadas) {
	    aux = c.estimular(aux);
	}

	return aux;
    }    
    
    public void criarCamadaNeural(int quantidadeNeuronios, int tipoNeuronio) {
	
	ICamadaNeuralFeedFoward camadaNeural = new CamadaNeuralFeedFowardMP();
	
	for(int i = 0; i < quantidadeNeuronios; i++) {
	    
	    camadaNeural.adicionarNeuronio(criarNeuronioGenerico(0.0, tipoNeuronio));
	}
	
	camadas.add(camadaNeural);
    }

    public void criarCamadasNeurais(int quantidadeCamadas, int[] quantidadeNeuronios, 
	    int[] tiposNeuronios, double[] limiares) 
    throws IllegalArgumentException {
	
	if(quantidadeCamadas != quantidadeNeuronios.length || quantidadeCamadas != tiposNeuronios.length) {
	    
	    throw new IllegalArgumentException("Quantidade de camadas deve ser igual aos elementos dos vetores de quantidade e tipo de neuronios!");
	}
	
	for(int i = 0; i < quantidadeCamadas; i++) {
	    criarCamadaNeural(quantidadeNeuronios[i], tiposNeuronios[i]);
	}
    }    
    
    public void adicionarCamadaNeural(ICamadaNeural camada) {

	camadas.add(camada);
    }
    
    public ICamadaNeural camadaNeural() {

	return camadas.get(indiceCamadas);
    }


    public ICamadaNeural proximaCamada() {

	indiceCamadas++;
	return camadaNeural();
    }    
    
    public ICamadaNeural primeiraCamada() {
	
	indiceCamadas = 0;
	return camadaNeural();	
    }
    
    public ICamadaNeural ultimaCamada() {
	
	indiceCamadas = camadas.size() -1;
	return camadaNeural();
    }

    public void removerCamadaNeural(ICamadaNeural camada) {
	
	camadas.remove(camada);
    }

    public boolean temCamadas() {
	
	return (indiceCamadas < camadas.size());
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {

	StringBuffer b = new StringBuffer("");
	int contCamadas = 1;
	
	for(ICamadaNeural camada : camadas) {
	
	    b.append("\n\n[Camada " + (contCamadas++) + "]");
	    b.append(camada);
	}
	
	return b.toString();
    }

    public void treinar(double[][] entradas, double[][] saidas) {
	
	for(ICamadaNeural camada : camadas) {
	    camada.treinar(entradas, saidas);
	}
    }

}
